﻿using Hx.ADSyncPlatform.ActiveDirectory.Access;
using Hx.ADSyncPlatform.ActiveDirectory.Extension;
using Hx.ADSyncPlatform.ActiveDirectory.Model;
using Hx.ADSyncPlatform.ActiveDirectory.Service.Base;
using Hx.ADSyncPlatform.Entity.Entities;
using Hx.ADSyncPlatform.Infrastructure.Page;
using System;
using System.Collections.Generic;
using System.DirectoryServices;
using System.DirectoryServices.ActiveDirectory;
using System.Linq;
using System.Text;

namespace Hx.ADSyncPlatform.ActiveDirectory.Service
{
    /// <summary>
    /// 组织
    /// </summary>
    public class OrganizationService : BaseService
    {

        private ADConnection ADCInfo;
        public OrganizationService(ADConnection _adcInfo)
        {
            this.ADCInfo = _adcInfo;
        }

        /// <summary>
        /// 单个组织
        /// </summary>
        /// <param name="cn">组织CN</param>
        /// <param name="searchLocation">查找位置</param>
        /// <returns>单个组织信息</returns>
        public Organization Single(string cn)
        {
            using (var adc = new ADConnection(ADCInfo.LDAPPath, ADCInfo.UserName, ADCInfo.Password))
            {
                var orgDe = new OrganizationAccess().Single(adc.RootEntry, cn);
                return orgDe == null ? null : orgDe.Properties?.ConvertTo<Organization>();
            }
        }

        /// <summary>
        /// 单个组织
        /// </summary>
        /// <param name="cn">组织CN</param>
        /// <param name="searchLocation">查找位置</param>
        /// <returns>单个组织信息</returns>
        public Organization Single(string orgCn, string cn)
        {
            using (var adc = new ADConnection(ADCInfo.LDAPPath, ADCInfo.UserName, ADCInfo.Password))
            {
                var orgDe = new OrganizationAccess().Single(adc.RootEntry, orgCn);

                var de = new OrganizationAccess().Single(orgDe.GetDirectoryEntry(), cn, SearchScope.OneLevel);
                return de == null ? null : de.Properties?.ConvertTo<Organization>();
            }
        }

        /// <summary>
        /// 查询组织
        /// 根据组织名模糊查询
        /// </summary>
        /// <param name="name">组织名</param>
        /// <param name="searchLocation">查询位置</param>
        /// <returns>组织集合</returns>
        public Organization Root()
        {
            using (var adc = new ADConnection(ADCInfo.LDAPPath, ADCInfo.UserName, ADCInfo.Password))
            {
                return adc.RootEntry.Properties.ConvertTo<Organization>();

            }
        }

        /// <summary>
        /// 查询组织
        /// 根据组织名模糊查询
        /// </summary>
        /// <param name="name">组织名</param>
        /// <param name="searchLocation">查询位置</param>
        /// <returns>组织集合</returns>
        public List<Organization> Get(string name)
        {
            using (var adc = new ADConnection(ADCInfo.LDAPPath, ADCInfo.UserName, ADCInfo.Password))
            {
                var orgs = new OrganizationAccess().Get(adc.RootEntry, name);

                List<Organization> data = new List<Organization>();
                //hack：if 缩进
                if (orgs == null || orgs.Count == 0)
                {
                    return data;
                }

                foreach (SearchResult org in orgs)
                {
                    var orgItem = org?.Properties?.ConvertTo<Organization>();
                    if (orgItem != null)
                    {
                        data.Add(orgItem);
                    }
                }
                return data;
            }
        }

        /// <summary>
        /// 查询组织
        /// 根据组织名、显示名称模糊查询
        /// </summary>
        /// <param name="name">组织名</param>
        /// <param name="displayName">显示名</param>
        /// <param name="searchLocation">查询位置</param>
        /// <returns>组织集合</returns>
        public List<Organization> Get(string name, string displayName)
        {
            using (var adc = new ADConnection(ADCInfo.LDAPPath, ADCInfo.UserName, ADCInfo.Password))
            {
                var orgs = new OrganizationAccess().Get(adc.RootEntry, name, displayName);
                List<Organization> data = new List<Organization>();
                //hack：if 缩进
                if (orgs == null || orgs.Count == 0)
                {
                    return data;
                }

                foreach (SearchResult org in orgs)
                {
                    var orgItem = org?.Properties?.ConvertTo<Organization>();
                    if (orgItem != null)
                    {
                        data.Add(orgItem);
                    }
                }
                return data;
            }
        }



        /// <summary>
        /// 查询组织
        /// 查询某组织下的组织列表
        /// </summary>
        /// <param name="orgCn">组织CN</param>
        /// <param name="searchScope">查询层级</param>
        /// <param name="searchLocation">查询位置</param>
        /// <returns></returns>
        public List<Organization> Get(string orgCn, SearchScopeDerivation searchScope, List<string> excludeOrgs = null)
        {
            using (var adc = new ADConnection(ADCInfo.LDAPPath, ADCInfo.UserName, ADCInfo.Password))
            {
                var access = new OrganizationAccess();
                var orgDe = access.Single(adc.RootEntry, orgCn);

                if (orgDe == null)
                {
                    //hack：此处用NullReferenceException不合适
                    throw new ActiveDirectoryObjectNotFoundException($"组织【{orgCn}】不存在，查询无法继续。");
                }
                var orgChilds = access.Get(orgDe.GetDirectoryEntry(), searchScope.Convert(), excludeOrgs);
                List<Organization> data = new List<Organization>();
                if (orgChilds == null || orgChilds.Count == 0)
                {
                    return data;
                }
                foreach (SearchResult orgChild in orgChilds)
                {
                    var orgItem = orgChild?.Properties?.ConvertTo<Organization>();
                    if (orgItem != null)
                    {
                        data.Add(orgItem);
                    }
                }
                return data;
            }
        }


        /// <summary>
        /// 查询组织
        /// 查询某组织下的组织列表
        /// </summary>
        /// <param name="orgCn">组织CN</param>
        /// <param name="searchScope">查询层级</param>
        /// <param name="searchLocation">查询位置</param>
        /// <returns></returns>
        public List<Organization> GetByDN(string dn, SearchScopeDerivation searchScope, List<string> excludeOrgs = null)
        {
            using (var adc = new ADConnection(ADCInfo.LDAPPath, ADCInfo.UserName, ADCInfo.Password))
            {
                var access = new OrganizationAccess();
                var orgDe = access.SingleByDistinguishedName(adc.RootEntry, dn);

                if (orgDe == null)
                {
                    //hack：此处用NullReferenceException不合适
                    throw new ActiveDirectoryObjectNotFoundException($"组织DN【{dn}】不存在，查询无法继续。");
                }
                var orgChilds = access.Get(orgDe.GetDirectoryEntry(), searchScope.Convert(), excludeOrgs);
                List<Organization> data = new List<Organization>();
                if (orgChilds == null || orgChilds.Count == 0)
                {
                    return data;
                }
                foreach (SearchResult orgChild in orgChilds)
                {
                    var orgItem = orgChild?.Properties?.ConvertTo<Organization>();
                    if (orgItem != null)
                    {
                        data.Add(orgItem);
                    }
                }
                return data;
            }
        }
        /// <summary>
        /// 查询组织
        /// 查询某组织下的组织列表
        /// </summary>
        /// <param name="orgCn">组织CN</param>
        /// <param name="searchScope">查询层级</param>
        /// <param name="searchLocation">查询位置</param>
        /// <returns></returns>
        public List<Organization> Get(string orgCn, string displayName, List<Plat_DataPermission> dataPermissions, string searchScope = "current")
        {
            using (var adc = new ADConnection(ADCInfo.LDAPPath, ADCInfo.UserName, ADCInfo.Password))
            {
                var access = new OrganizationAccess();
                var orgDe = access.Single(adc.RootEntry, orgCn);

                if (orgDe == null)
                {
                    //hack：此处用NullReferenceException不合适
                    throw new ActiveDirectoryObjectNotFoundException($"组织【{orgCn}】不存在，查询无法继续。");
                }
                StringBuilder sb = new StringBuilder();
                sb.Append("(&(objectclass=organizationalUnit)");
                if (!string.IsNullOrEmpty(displayName))
                {
                    sb.Append($"(name=*{displayName}*)");
                }
                sb.Append(")");
                SearchResultCollection src = null;
                if (searchScope == "current")
                {
                    src = new OrganizationAccess().Search(orgDe.GetDirectoryEntry(), sb.ToString(), null, SearchScope.OneLevel);
                }
                else if (searchScope == "global")
                {
                    src = new OrganizationAccess().Search(adc.RootEntry, sb.ToString(), null, SearchScope.Subtree);
                }
                var searchResults = src.OfType<SearchResult>();
                if (base.filterOrgByDataPermission != null)
                {
                    searchResults = filterOrgByDataPermission(searchResults, dataPermissions);
                }
                //var orgChilds = access.Get(orgDe.GetDirectoryEntry(), searchScope.Convert(), excludeOrgs);
                List<Organization> data = new List<Organization>();
                if (src == null || src.Count == 0)
                {
                    return data;
                }
                foreach (SearchResult orgChild in searchResults)
                {
                    var orgItem = orgChild?.Properties?.ConvertTo<Organization>();
                    if (orgItem != null)
                    {
                        data.Add(orgItem);
                    }
                }
                return data;
            }
        }
        /// <summary>
        /// 查询组织
        /// 查询某组织下的组织列表
        /// </summary>
        /// <param name="orgCn">组织CN</param>
        /// <param name="searchScope">查询层级</param>
        /// <param name="searchLocation">查询位置</param>
        /// <returns></returns>
        public bool HasChildren(string dn, List<string> excludeOrgs, List<Plat_DataPermission> plat_DataPermissions, bool useFilter = true)
        {
            using (var adc = new ADConnection(ADCInfo.LDAPPath, ADCInfo.UserName, ADCInfo.Password))
            {
                var access = new OrganizationAccess();
                var orgDe = access.SingleByDistinguishedName(adc.RootEntry, dn);

                string filter = string.Format("(&(objectclass=organizationalUnit))");

                if (excludeOrgs != null && excludeOrgs.Any())//排除调指定org
                {
                    string excludeFilter = "";
                    foreach (string excludeStr in excludeOrgs)
                    {
                        excludeFilter += $"(!(cn={excludeStr}))";
                    }
                    filter = $"(&(objectclass=organizationalUnit){excludeFilter})";
                }
                DirectorySearcher dsearcher = new DirectorySearcher(orgDe.GetDirectoryEntry(), filter, null, SearchScope.OneLevel);
                SearchResultCollection results = dsearcher.FindAll();
                IEnumerable<SearchResult> searchResults = results.OfType<SearchResult>();

                if (useFilter && base.filterOrgByDataPermission != null)
                {
                    searchResults = filterOrgByDataPermission(searchResults, plat_DataPermissions);
                }
                return searchResults.Any();
            }
        }


        /// <summary>
        /// 查询人员分页
        /// </summary>
        /// <param name="orgCn">组织CN</param>
        /// <param name="displayName">显示名</param>
        /// <param name="pageIndex"></param>
        /// <param name="pageSize"></param>
        /// <returns>人员分页集合</returns>
        public PageResult<Organization> PageList(string parentCn, string displayName, List<Plat_DataPermission> dataPermissions, string searchScope = "current", int pageIndex = 1, int pageSize = 10)
        {
            using (var adc = new ADConnection(ADCInfo.LDAPPath, ADCInfo.UserName, ADCInfo.Password))
            {
                var access = new OrganizationAccess();
                var orgDe = access.Single(adc.RootEntry, parentCn);

                if (orgDe == null)
                {
                    //hack：此处用NullReferenceException不合适
                    throw new ActiveDirectoryObjectNotFoundException($"组织【{parentCn}】不存在，查询无法继续。");
                }
                StringBuilder sb = new StringBuilder();
                sb.Append("(&(objectclass=organizationalUnit)");
                if (!string.IsNullOrEmpty(displayName))
                {
                    sb.Append($"(name=*{displayName}*)");
                }
                sb.Append(")");
                SearchResultCollection src = null;
                if (searchScope == "current")
                {
                    src = new OrganizationAccess().Search(orgDe.GetDirectoryEntry(), sb.ToString(), null, SearchScope.OneLevel);
                }
                else if (searchScope == "global")
                {
                    src = new OrganizationAccess().Search(adc.RootEntry, sb.ToString(), null, SearchScope.Subtree);
                }

                var searchResults = src.OfType<SearchResult>();
                if (base.filterOrgByDataPermission != null)
                {
                    searchResults = filterOrgByDataPermission(searchResults, dataPermissions);
                }
                int total = searchResults.Count();
                int startNum = Math.Max(pageIndex - 1, 0) * pageSize;
                searchResults = searchResults.Skip(startNum).Take(pageSize);

                var personList = searchResults.Select(n =>
                {
                    return n.Properties?.ConvertTo<Organization>();

                }).ToList();

                PageResult<Organization> pageResult = new PageResult<Organization>(personList, pageIndex, total);
                return pageResult;


            }
        }
        /// <summary>
        /// 自定义查询
        /// </summary>
        /// <param name="filter">LDAP语句</param>
        /// <param name="propertiesToLoad">需要返回的属性</param>
        /// <param name="searchScope">查询层级</param>
        /// <param name="pagination">分页信息</param>
        /// <returns>结果集以及分页信息</returns>
        public PageResult<Organization> Search(string filter, List<string> propertiesToLoad, SearchScopeDerivation searchScope, int pageIndex, int pageSize)
        {
            using (var adc = new ADConnection(ADCInfo.LDAPPath, ADCInfo.UserName, ADCInfo.Password))
            {
                var src = new OrganizationAccess().Search(adc.RootEntry, filter, propertiesToLoad, searchScope.Convert(), pageIndex * pageSize);

                int total = src.Count;
                int startNum = Math.Max(pageIndex - 1, 0) * pageSize;
                var searchResults = src.OfType<SearchResult>().Skip(startNum).Take(pageSize).ToList();

                var orgList = searchResults.Select(n =>
                {
                    return n.Properties?.ConvertTo<Organization>();

                }).ToList();

                PageResult<Organization> pageResult = new PageResult<Organization>(orgList, pageIndex, total);
                return pageResult;
            }
        }

        /// <summary>
        /// 创建组织
        /// </summary>
        /// <param name="orgCn">创建在某组织下</param>
        /// <param name="org">组织信息</param>
        /// <returns>成功True，失败False</returns>
        public bool Create(string orgCn, Organization org)
        {
            using (var adc = new ADConnection(ADCInfo.LDAPPath, ADCInfo.UserName, ADCInfo.Password))
            {
                var access = new OrganizationAccess();
                var orgDe = access.Single(adc.RootEntry, orgCn);
                if (orgDe == null)
                {
                    //hack：此处用NullReferenceException不合适
                    throw new ActiveDirectoryObjectNotFoundException($"组织【{orgCn}】不存在，创建组织无法继续。");
                }
                return access.Create(orgDe.GetDirectoryEntry(), org);
            }
        }

        /// <summary>
        /// 修改组织
        /// </summary>
        /// <param name="org">
        /// 修改的组织信息
        /// </param>
        /// <returns>成功True，失败False</returns>
        public bool Update(Organization org)
        {
            using (var adc = new ADConnection(ADCInfo.LDAPPath, ADCInfo.UserName, ADCInfo.Password))
            {
                var access = new OrganizationAccess();
                var orgDe = access.Single(adc.RootEntry, org.CN);
                if (orgDe == null)
                {
                    //hack：此处用NullReferenceException不合适
                    throw new ActiveDirectoryObjectNotFoundException($"组织【{org.CN}】不存在，更新组织无法继续。");
                }

                return access.Update(orgDe.GetDirectoryEntry(), org);
            }
        }

        /// <summary>
        /// 移动组织
        /// </summary>
        /// <param name="cn">组织组织CN</param>
        /// <param name="orgCn">目标组织CN</param>
        /// <returns>成功True，失败False</returns>
        public bool Move(string cn, string orgCn)
        {
            using (var adc = new ADConnection(ADCInfo.LDAPPath, ADCInfo.UserName, ADCInfo.Password))
            {
                var access = new OrganizationAccess();
                var orgDe = access.Single(adc.RootEntry, cn);

                if (orgDe == null)
                {
                    //hack：此处用NullReferenceException不合适
                    throw new ActiveDirectoryObjectNotFoundException($"组织【{cn}】不存在，移动组织无法继续。");
                }
                DirectoryEntry org = orgDe.GetDirectoryEntry();

                var targetOrgDe = access.Single(adc.RootEntry, orgCn);
                if (targetOrgDe == null)
                {
                    //hack：此处用NullReferenceException不合适
                    throw new ActiveDirectoryObjectNotFoundException($"目标组织【{orgCn}】不存在，移动组织无法继续。");
                }
                DirectoryEntry targetOrg = targetOrgDe.GetDirectoryEntry();

                return access.Move(targetOrg, org);
            }
        }

        /// <summary>
        /// 删除组织
        /// </summary>
        /// <param name="name">组织名</param>
        /// <returns>成功True，失败False</returns>
        public bool Delete(string cn)
        {
            using (var adc = new ADConnection(ADCInfo.LDAPPath, ADCInfo.UserName, ADCInfo.Password))
            {
                var access = new OrganizationAccess();
                var orgDe = access.Single(adc.RootEntry, cn);

                if (orgDe == null)
                {
                    //hack：此处用NullReferenceException不合适
                    throw new ActiveDirectoryObjectNotFoundException($"组织【{cn}】不存在，无法删除。");
                }

                DirectoryEntry org = orgDe.GetDirectoryEntry();
                //hack:如果存在子级也要删除怎么办？
                //var seacher = new DirectorySearcher(org)
                //{
                //    Filter =
                // "(|(objectcategory=organizationalUnit)(objectcategory=person)(objectcategory=group)(objectcategory=computer))",
                //    SearchScope = SearchScope.OneLevel
                //};

                //var searchResult = seacher.FindOne();
                //if (searchResult != null)
                //    //hack：存在子级也用NullReferenceException？？？
                //    throw new ActiveDirectoryObjectExistsException($"组织【{cn}】存在子级，无法删除。");
                return access.Delete(org);
            }
        }

        /// <summary>
        /// 组织下是否存在子级，子级包括：人、组、组织、计算机
        /// </summary>
        /// <param name="cn">组织CN</param>
        /// <returns>True：存在，False：不存在</returns>
        public bool ExistChildren(string cn)
        {
            using (var adc = new ADConnection(ADCInfo.LDAPPath, ADCInfo.UserName, ADCInfo.Password))
            {
                var access = new OrganizationAccess();
                var orgDe = access.Single(adc.RootEntry, cn);

                if (orgDe == null)
                {
                    return false;
                }

                DirectoryEntry org = orgDe.GetDirectoryEntry();

                var seacher = new DirectorySearcher(org)
                {
                    Filter =
                 "(|(objectcategory=organizationalUnit)(objectcategory=person)(objectcategory=group)(objectcategory=computer))",
                    SearchScope = SearchScope.OneLevel
                };

                var searchResult = seacher.FindOne();
                if (searchResult != null)
                    return true;
                return false;
            }
        }



    }
}
